package com.abgame.test.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.websocket.*;
import org.glassfish.tyrus.client.ClientManager;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
@ClientEndpoint
public class WebSocketClient {

    private static final String WEBSOCKET_URI = "wss://broadcastlv.chat.bilibili.com/sub";
    private static final int UID = 0;
    private static final long ROOM_ID = 1234567L;
    private static final int PROTOVER = 1;
    private static final String PLATFORM = "web";
    private static final String CLIENT_VER = "1.4.0";
    private static final String HEARTBEAT_TYPE = "heartbeat";
    private static final int MAX_RECONNECT_ATTEMPTS = 5;
    private static final long RECONNECT_INTERVAL = 5;

    private Session session;
    private ScheduledExecutorService heartbeatExecutor;
    private ObjectMapper objectMapper = new ObjectMapper();
    private int reconnectAttempts = 0;
    private ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor();

    public void connect() {
        try {
            // 使用 Tyrus 的 ClientManager 创建 WebSocket 连接
            ClientManager client = ClientManager.createClient();
            client.connectToServer(this, new URI(WEBSOCKET_URI));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        System.out.println("Connected to server");
        sendInitialMessage();
        startHeartbeat();
        reconnectAttempts = 0;
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("Received message: " + message);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("Connection closed: " + closeReason);
        stopHeartbeat();
        scheduleReconnect();
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("Error: " + throwable.getMessage());
        stopHeartbeat();
        scheduleReconnect();
    }

    private void sendInitialMessage() {
        try {
            Message message = new Message(UID, ROOM_ID, PROTOVER, PLATFORM, CLIENT_VER);
            String jsonMessage = objectMapper.writeValueAsString(message);
            this.session.getBasicRemote().sendText(jsonMessage);
            System.out.println("Sent initial message: " + jsonMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void startHeartbeat() {
        heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();
        heartbeatExecutor.scheduleAtFixedRate(() -> {
            try {
                Message heartbeatMessage = new Message(UID, ROOM_ID, PROTOVER, PLATFORM, CLIENT_VER, HEARTBEAT_TYPE);
                String jsonMessage = objectMapper.writeValueAsString(heartbeatMessage);
                session.getBasicRemote().sendText(jsonMessage);
                System.out.println("Sent heartbeat message: " + jsonMessage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 30, 30, TimeUnit.SECONDS);
    }

    private void stopHeartbeat() {
        if (heartbeatExecutor != null && !heartbeatExecutor.isShutdown()) {
            heartbeatExecutor.shutdown();
        }
    }

    private void scheduleReconnect() {
        if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
            reconnectAttempts++;
            System.out.println("Attempting to reconnect... (" + reconnectAttempts + "/" + MAX_RECONNECT_ATTEMPTS + ")");
            reconnectExecutor.schedule(this::connect, RECONNECT_INTERVAL, TimeUnit.SECONDS);
        } else {
            System.out.println("Max reconnect attempts reached. Giving up.");
        }
    }

    private static class Message {
        public int uid;
        public long roomid;
        public int protover;
        public String platform;
        public String clientver;
        public String type;

        public Message(int uid, long roomid, int protover, String platform, String clientver) {
            this.uid = uid;
            this.roomid = roomid;
            this.protover = protover;
            this.platform = platform;
            this.clientver = clientver;
        }

        public Message(int uid, long roomid, int protover, String platform, String clientver, String type) {
            this.uid = uid;
            this.roomid = roomid;
            this.protover = protover;
            this.platform = platform;
            this.clientver = clientver;
            this.type = type;
        }
    }
}
